import 'dart:async';

import 'package:rxdart/rxdart.dart';

import '../../common/test_page.dart';
import '../utils.dart';

Stream<int> _getStream() => Stream<int>.fromIterable(<int>[0, 1, 2]);

class TimeTestPage extends TestPage {
  TimeTestPage(super.title) {
    test('Rx.timeInterval', () async {
      const expectedOutput = [0, 1, 2];
      var count = 0;

      _getStream()
          .interval(const Duration(milliseconds: 1))
          .timeInterval()
          .listen(((result) {
        expect(expectedOutput[count++]);
        expect(result.interval.inMicroseconds >= 1000 );
      }));
    });

    test('Rx.timeInterval.reusable', () async {
      final transformer = TimeIntervalStreamTransformer<int>();
      const expectedOutput = [0, 1, 2];
      var countA = 0, countB = 0;

      _getStream()
          .interval(const Duration(milliseconds: 1))
          .transform(transformer)
          .listen(((result) {
        expect(expectedOutput[countA++]);
        expect(result.interval.inMicroseconds >= 1000);
      }));

      _getStream()
          .interval(const Duration(milliseconds: 1))
          .transform(transformer)
          .listen(((result) {
        expect(expectedOutput[countB++]);
        expect(result.interval.inMicroseconds >= 1000);
      }));
    });

    test('Rx.timeInterval.asBroadcastStream', () async {
      final stream = _getStream()
          .asBroadcastStream()
          .interval(const Duration(milliseconds: 1))
          .timeInterval();


      stream.listen(null);
      stream.listen(null);

      expect(true);
    });

    test('Rx.timeInterval.error.shouldThrow', () async {
      final streamWithError = Stream<void>.error(Exception())
          .interval(const Duration(milliseconds: 1))
          .timeInterval();

      streamWithError.listen(null,
          onError: (Object e, StackTrace s) {
            expect(e);
          });
    });

    test('Rx.timeInterval.pause.resume', () async {
      late StreamSubscription<TimeInterval<int>> subscription;
      const expectedOutput = [0, 1, 2];
      var count = 0;

      subscription = _getStream()
          .interval(const Duration(milliseconds: 1))
          .timeInterval()
          .listen(((result) {
        expect(result.value);
        if (count == expectedOutput.length) {
          subscription.cancel();
        }
      }));

      subscription.pause();
      subscription.resume();
    });

    test('Rx.timeInterval accidental broadcast', () async {
      final controller = StreamController<int>();

      final stream = controller.stream.timeInterval();

      stream.listen(null);
      expect(() => stream.listen(null));

      controller.add(1);
    });

    test('Rx.timeInterval.nullable', () {
      nullableTest<TimeInterval<String?>>(
            (s) => s.timeInterval(),
      );
    });

    test('Rx.timeout', () async {
      late StreamSubscription<int> subscription;

      final stream = Stream<int>.fromFuture(
          Future<int>.delayed(Duration(milliseconds: 30), () => 1))
          .timeout(Duration(milliseconds: 1));

      subscription = stream.listen((_) {},
          onError: ((Object e, StackTrace s) {
            expect(e is TimeoutException);
            subscription.cancel();
          }));
    });

    test('Rx.Rx.timestamp', () async {
      Stream.fromIterable(const [1, 2, 3])
          .timestamp()
          .listen(((result) {
        expect(result.value);
      }));
    });

    test('Rx.Rx.timestamp.reusable', () async {
      final transformer = TimestampStreamTransformer<int>();

      Stream.fromIterable(const [1, 2, 3])
          .transform(transformer)
          .listen(((result) {
        expect(result.value);
      }));

      Stream.fromIterable(const [1, 2, 3])
          .transform(transformer)
          .listen(((result) {
        expect(result.value);
      }));
    });

    test('timestampTransformer', () async {

      Stream.fromIterable(const [1, 2, 3])
          .transform(TimestampStreamTransformer<int>())
          .listen(((result) {
        expect(result.value);
      }));
    });

    test('timestampTransformer.asBroadcastStream', () async {
      final stream = Stream.fromIterable(const [1, 2, 3])
          .transform(TimestampStreamTransformer<int>())
          .asBroadcastStream();

      stream.listen(null);
      stream.listen(null);

      expect(stream.isBroadcast);
    });

    test('timestampTransformer.error.shouldThrow', () async {
      final streamWithError =
      Stream<int>.error(Exception()).transform(TimestampStreamTransformer());

      streamWithError.listen(null,
          onError: (Object e, StackTrace s) {
            expect(e);
          });
    });

    test('timestampTransformer.pause.resume', () async {
      final stream = Stream.fromIterable(const [1, 2, 3])
          .transform(TimestampStreamTransformer());
      const expected = [1, 2, 3];
      late StreamSubscription<Timestamped<int>> subscription;
      var count = 0;

      subscription = stream.listen(((result) {
        expect(result.value);

        if (count == expected.length) {
          subscription.cancel();
        }
      }));

      subscription.pause();
      subscription.resume();
    });
    test('Rx.timestamp accidental broadcast', () async {
      final controller = StreamController<int>();

      final stream = controller.stream.timestamp();

      stream.listen(null);
      expect(() => stream.listen(null));

      controller.add(1);
    });

    test('Rx.timestamp.nullable', () {
      nullableTest<Timestamped<String?>>(
            (s) => s.timestamp(),
      );
    });
  }

}